home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 1.iso
/
toolbox
/
src
/
exampleCode
/
opengl
/
extensions
/
samples
/
videobub.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-11
|
16KB
|
609 lines
/*
* Copyright (c) 1994 Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that (i) the above copyright notices and this permission
* notice appear in all copies of the software and related documentation,
* and (ii) the name of Silicon Graphics may not be used in any
* advertising or publicity relating to the software without the specific,
* prior written permission of Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL,
* INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
/*
* vidtobub.c
*
* Demonstrate video-to-texture transfers from Sirius in OpenGL.
*
* The program "simulates" soap bubbles floating in front of a live video
* image.
*
* Algorithm:
* The video frame are copied to texture memory.
*
* A polygon that covers the window is drawn with the texture mapped onto
* it in a "1-1" ratio for the underlying video image.
*
* The "bubbles" are sphere like spline meshes that move across the image.
* The see through and "lens" warping is achieved by mapping the texture onto
* the bubble with some streching of the texture coordinates, thus mapping
* "more" texture onto the bubble. To movement of the bubble across the image
* is accounted for by adjusting the texture matrix for each bubble according
* to the relative position of the bubble.
*
* See the usage() function for the command line options (or run with -h).
*
* $Revision: 1.1 $
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <getopt.h>
#include <device.h>
#include <vl/vl.h>
#include <vl/dev_sirius.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <X11/keysym.h>
#include "xwindow.h"
#define XSIZE 64.0
#define YSIZE 64.0
#define WINSCALE 1.8
typedef struct {
int ssize, tsize;
int x, y;
int dx, dy;
} Bubble;
GLfloat texture_matrix[4][4] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
/*
* texture variables
*/
float s_scale, t_scale, s_fraction, t_fraction;
/*
* VL variables
*/
VLControlValue size, timing;
VLControlValue format;
VLControlValue cap_type;
VLServer svr;
VLPath path;
VLNode src;
VLNode drn;
/* OpenGL/X variables */
Display *dpy;
Window window;
#ifdef GLX_SGIX_video_source
GLXVideoSourceSGIX glxVideoSource;
#else /* Make the compiler barf */
GLX_SGIX_video_source is not defined in this version of the library
#endif
/*
* function prototypes
*/
void usage(char *, int);
void initVideo(int, int, int);
void initGfx(int, char **);
int CreateTexture(void);
void DisplayNewFrame(void);
void cleanup(void);
void ProcessEvents(void);
void UpdateTimingFormat(void);
int t_width, t_height;
/**************************************************************************/
#define ORDER 4
#define SEGMENTS 12
int s_points, t_points;
int s_stride, t_stride;
int comps = 3;
int drawmap = 1;
GLfloat *tmap, *vmap;
int numBubbles = 0;
Bubble bubs[100];
/*
* Create a bubble at x,y with randomize speed.
*/
void
make_bubble(int x, int y)
{
Bubble *b = &bubs[numBubbles];
b->ssize = b->tsize = 70 + (unsigned)(rand() & 0x3c);
b->x = x/WINSCALE - b->ssize/2;
b->y = (size.xyVal.y * WINSCALE - y - 1)/WINSCALE - b->tsize/2;
b->dx = (2 + (rand() & 0x03)) * (rand() & 0x1 ? 1 : -1 );
b->dy = (2 + (rand() & 0x03)) * (rand() & 0x1 ? 1 : -1 );
numBubbles++;
}
/*
* Setup the evaluator maps that are going to be used for drawing the
* bubbles.
*/
void
setupMaps(void)
{
int s, t;
float cs, ct, s1, t1, d0, d1, mx, scale;
double fs1, ft1;
GLfloat *vertcp, *texcp;
s_points = ORDER;
t_points = ORDER;
s_stride = comps;
t_stride = comps * s_points;
vertcp = vmap = malloc(s_points * t_points * comps * sizeof(GLfloat));
texcp = tmap = malloc(s_points * t_points * comps * sizeof(GLfloat));
cs = (s_points - 1)/2.0;
ct = (t_points - 1)/2.0;
for (t=0; t < t_points; t++) {
for (s=0; s < s_points; s++) {
/* distances from control point to center of patch */
s1 = s - cs;
t1 = t - ct;
fs1 = fabs(s1);
ft1 = fabs(t1);
/* warp vertex control points to make it a disk */
if (s1 == 0 && t1 == 0) {
scale = 1;
} else {
mx = fs1 > ft1 ? fs1 : ft1;
if (fs1 == ft1) {
/* extra tug at the corners to make it more round */
mx *= 0.85;
}
scale = mx/sqrt(s1 * s1 + t1 * t1);
}
vertcp[0] = cs + (s1 * scale);
vertcp[1] = ct + (t1 * scale);
vertcp[2] = 0;
vertcp += comps;
/* warp texture coord control points to make it a bubble */
if (s1 == 0 && t1 == 0) {
scale = 1;
} else {
mx = fs1 > ft1 ? fs1 : ft1;
if (mx == cs - 2 || mx == 0.5) {
/* push outward */
mx += 8;
} else if (mx == cs - 1) {
/* push inward */
mx += -0.5;
}
scale = fabs(mx)/sqrt(s1 * s1 + t1 * t1);
}
texcp[0] = cs + (s1 * scale);
texcp[1] = ct + (t1 * scale);
texcp[2] = 0;
texcp += comps;
}
}
glMapGrid2f(SEGMENTS, 0, 1, SEGMENTS, 0, 1);
}
unsigned char white[] = { 0xff, 0xff, 0xff };
unsigned char gray[] = { 0x80, 0x80, 0x80 };
int ssize = 1024, tsize = 512;
/*
* Draw a single bubble by mapping the texture with the correct offset onto
* the bubble.
*/
void
drawBubble( Bubble *bub )
{
int is, it;
float *p;
float bw, bh, bx, by, s0, s1, t0, t1;
bw = bub->ssize;
bh = bub->tsize;
bx = bub->x;
by = bub->y;
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat *) texture_matrix);
glScalef(1.0/size.xyVal.x, 1.0/size.xyVal.y, 1);
glTranslatef(bx, by, 0);
glScalef(bw/(s_points - 1), bh/(t_points - 1), 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(bx, by, 0);
glScalef(bw/(s_points - 1), bh/(t_points - 1), 1);
glEnable(GL_TEXTURE_2D);
glEnable(GL_MAP2_TEXTURE_COORD_2);
glEnable(GL_MAP2_VERTEX_3);
for (it=0; it < 1; it++) {
for (is=0; is < 1; is++) {
p = vmap + it * t_stride * (ORDER - 1) +
is * s_stride * (ORDER - 1);
glMap2f(GL_MAP2_VERTEX_3, 0, 1,
s_stride, 4, 0, 1, t_stride, 4, p);
p = tmap + it * t_stride * (ORDER - 1) +
is * s_stride * (ORDER - 1);
glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1,
s_stride, 4, 0, 1, t_stride, 4, p);
glEvalMesh2(GL_FILL, 0, SEGMENTS, 0, SEGMENTS);
}
}
glDisable(GL_TEXTURE_2D);
bub->x += bub->dx;
bub->y += bub->dy;
if (bub->x > size.xyVal.x)
bub->x = 0;
else if (bub->x < 0)
bub->x = size.xyVal.x;
if (bub->y > size.xyVal.y)
bub->y = 0;
else if (bub->y < 0)
bub->y = size.xyVal.y;
}
/**************************************************************************/
int
main(int argc, char **argv)
{
int c, insrc = VL_ANY;
int device = VL_ANY;
short dev, val;
int packingFormat = 5;
/* Parse the input string */
while ((c = getopt(argc, argv, "n:v:t:s:i:o:M:hFG")) != EOF ) {
switch (c) {
case 'n':
device = atoi(optarg); /* user can override default device */
break;
case 't':
packingFormat = atoi(optarg);
break;
case 'v':
insrc = atoi(optarg);
break;
case 'h':
usage(argv[0], EXIT_SUCCESS);
default:
usage(argv[0], EXIT_FAILURE);
}
}
initVideo(insrc, device, packingFormat);
initGfx(argc, argv);
vlBeginTransfer(svr, path, 0, NULL);
while (TRUE) {
if (XEventsQueued(dpy, QueuedAfterReading)) {
XEvent event;
XNextEvent(dpy, &event);
switch (event.type) {
case ButtonPress:
make_bubble(((XButtonEvent *) &event)->x,
((XButtonEvent *) &event)->y);
break;
case KeyPress: {
KeySym key;
(void) XLookupString(&event.xkey, NULL, 0, &key, NULL);
switch (key) {
case XK_Escape:
exit(EXIT_FAILURE);
break;
case XK_h:
case XK_H:
case XK_question:
printf("Keys:\Mouse buttons - create bubbles\n");
printf("\th/? - help\n");
printf("\tEsc - quit\n");
break;
}
}
}
}
/* Check for VL events if any ... */
ProcessEvents();
/* Next frame of data */
DisplayNewFrame();
glXSwapBuffers(dpy, window);
}
}
void
usage(char *name, int exitStatus)
{
fprintf(stderr, "usage: %s [-n#] [-v#] [-t#] [-h (for help)]\n", name);
fprintf(stderr, "\t-n# video device number (as reported by vlinfo)\n");
fprintf(stderr, "\t-v# video input number (as reported by vlinfo)\n");
fprintf(stderr, "\t-t# texture format (as packed by Sirius)\n");
fprintf(stderr,
"\tInteractively, `h/?' keys provide description of interface\n");
exit(exitStatus);
}
void
initVideo(int insrc, int device, int packingFormat)
{
VLControlValue tex;
/* open the server */
if (!(svr = vlOpenVideo(""))) {
printf("couldn't open video\n");
exit(EXIT_FAILURE);
}
/* Get the Video source */
src = vlGetNode(svr, VL_SRC, VL_VIDEO, insrc);
/* Get the Texture drain */
drn = vlGetNode(svr, VL_DRN, VL_TEXTURE, 0);
/* Create path */
path = vlCreatePath(svr, device, src, drn);
if (path < 0) {
vlPerror("vlCreatePath");
exit(EXIT_FAILURE);
}
/* setup path */
if (vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE, VL_SHARE) < 0) {
vlPerror("vlSetupPaths");
exit(EXIT_FAILURE);
}
/* select the appropriate events */
if (vlSelectEvents(svr, path, VLStreamPreemptedMask |
VLControlChangedMask ) < 0) {
vlPerror("Select Events");
exit(EXIT_FAILURE);
}
cap_type.intVal = VL_CAPTURE_INTERLEAVED;
if (vlSetControl(svr, path, drn, VL_CAP_TYPE, &cap_type) <0) {
vlPerror("VlSetControl");
exit(EXIT_FAILURE);
}
UpdateTimingFormat();
/* Set the Texture packing mode */
switch(packingFormat) {
case 4:
tex.intVal = SIR_TEX_PACK_RGBA_4;
break;
case 5:
tex.intVal = SIR_TEX_PACK_RGB_5;
break;
case 8:
tex.intVal = SIR_TEX_PACK_RGBA_8;
break;
default:
tex.intVal = SIR_TEX_PACK_RGB_5;
break;
}
if (vlSetControl(svr, path, drn, VL_PACKING, &tex) <0) {
vlPerror("VlSetControl");
exit(EXIT_FAILURE);
}
}
/*
* InitGfx:
* Initialize the graphics.
* Window size is base of the video format as determined by
* the timing bvaluse on the source node.
*/
void
initGfx(int argc, char **argv)
{
GLXContext ctx;
int visualAttr[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1,
GLX_DEPTH_SIZE, 0, GLX_STENCIL_SIZE, 0,
None};
createWindowAndContext(&dpy, &window, &ctx, 50, 0, size.xyVal.x * WINSCALE,
size.xyVal.y * WINSCALE, GL_FALSE, NULL,
visualAttr, argv[0]);
glxVideoSource = glXCreateGLXVideoSourceSGIX(dpy, DefaultScreen(dpy), svr,
path, VL_TEXTURE, drn);
if (glxVideoSource == None) {
fprintf(stderr, "can't create video source\n");
exit(EXIT_FAILURE);
}
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat *) texture_matrix);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-10, size.xyVal.x + 10, -10, size.xyVal.y + 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -0.5);
glXMakeCurrentReadSGI(dpy, window, glxVideoSource, ctx);
CreateTexture();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
setupMaps();
glColor4f(1, 1, 1, 1);
}
/*
* CreateTexture: Create a texture map with correct attributes.
*/
int
CreateTexture()
{
GLubyte fakeTexture[1024][1024][3];
/*
* 625 textures are bigger than 525 textures
*/
if (timing.intVal == VL_TIMING_525_SQ_PIX ||
timing.intVal == VL_TIMING_525_CCIR601) {
t_width = 1024;
t_height = 512;
} else {
t_width = 1024;
t_height = 1024;
}
s_scale = (size.xyVal.x - 1)/(float) t_width;
t_scale = size.xyVal.y/(float) t_height;
/* Sirius always transfers 768 pixels */
s_fraction = 768.0/t_width;
t_fraction = size.xyVal.y/(float) t_height;
glEnable(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_EXT, t_width, t_height, 0,
GL_RGB, GL_UNSIGNED_BYTE, fakeTexture);
texture_matrix[0][0] = s_scale;
texture_matrix[1][1] = -t_scale;
texture_matrix[3][1] = t_scale;
}
/*
* DisplayFrame: Display a new frame.
* This routine is called at 30 Hz for 525 timing and 25Hz for 625 timing.
*/
void
DisplayNewFrame()
{
int i;
glClear(GL_COLOR_BUFFER_BIT);
/* Load the texture from Sirius to Texture memory */
glCopyTexSubImage2DEXT(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
768, size.xyVal.y);
/* First draw a full window texture mapped polygon */
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat*) texture_matrix);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -0.5);
glEnable(GL_TEXTURE_2D);
glBegin(GL_POLYGON);
glTexCoord2f(0, 0); glVertex2f(0, 0);
glTexCoord2f(1, 0); glVertex2f(size.xyVal.x, 0);
glTexCoord2f(1, 1); glVertex2f(size.xyVal.x, size.xyVal.y);
glTexCoord2f(0, 1); glVertex2f(0, size.xyVal.y);
glEnd();
/* Draw the bubbles */
for (i=0; i < numBubbles; i++)
drawBubble(&bubs[i]);
}
void
cleanup()
{
vlEndTransfer(svr, path);
vlDestroyPath(svr, path);
vlCloseVideo(svr);
exit(EXIT_SUCCESS);
}
void
ProcessEvents()
{
VLEvent ev;
/* Check to see if any controls changed or if the video
* path has been pre-empted.
*/
if (vlCheckEvent(svr, VLControlChangedMask|
VLStreamPreemptedMask, &ev) == -1) {
return;
}
switch(ev.reason) {
case VLStreamPreempted:
cleanup();
break;
case VLControlChanged:
switch(ev.vlcontrolchanged.type) {
case VL_TIMING:
case VL_SIZE:
case VL_PACKING:
UpdateTimingFormat();
printf("got VL_PACKING event\n");
break;
default:
break;
}
break;
default:
break;
}
}
void
UpdateTimingFormat()
{
/* Get the timing from input source */
if (vlGetControl(svr, path, src, VL_TIMING, &timing) < 0) {
vlPerror("vlGetControl");
exit(EXIT_FAILURE);
}
/* Set texture drain's timing to input source */
if (vlSetControl(svr, path, drn, VL_TIMING, &timing) < 0) {
vlPerror("vlSetControl");
exit(EXIT_FAILURE);
}
if (vlGetControl(svr, path, src, VL_SIZE, &size) < 0) {
vlPerror("vlSetupPaths");
exit(EXIT_FAILURE);
}
}